package com.lyhlmj.oop_client.ClientOperation.NetOperation;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;

/* 客户端的连接:
 * 	NetConnection conn = new NetConnection();
 *  conn.connect(...)
 * 服务端的连接:
 * 	NetConnection conn = new NetConnection(sock);
 */

//用来进行网络连接
public class NetConnection {
    public int maxPacketSize = 1024 * 1024; // 收到到包的最大为1M
    public String charset = "UTF-8";  // 字符集: UTF-8
    public Socket socket;

    private InputStream inputStream;
    private OutputStream outputStream;

    public NetConnection() {
    }

    public NetConnection(Socket socket) {
        this.socket = socket;

        try {
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("（服务器）数据流输入/输出创建失败！--");
        }
    }

    // （前端）连接至服务器
    public void connect(String ip, int port, int timeout) {
        socket = new Socket();

        try {
            socket.connect(new InetSocketAddress(ip, port), timeout);
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("前端连接创建失败！");
        }
    }

    // 关闭连接
    public void close() {
        try {
            socket.close();
            socket = null;
        } catch (Exception e) {
            System.out.println("连接关闭失败！");
        }
    }

    //    对请求读取进行超时处理
    public void setSoTimeout(int timeout) {
        try {
            socket.setSoTimeout(timeout);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    ///////////////  数据包的发送 ////////////
    // 只有消息类型，不带消息体
    public void send(short action) throws Exception{
        send(action, (String) null);
    }

    // 发送文本数据
    public void send(short action, String msg) throws Exception{
        send(action, msg, this.charset);
    }

    public void send(short action, String msg, String charset) throws Exception{
        NetPacket packet = new NetPacket(
                NetPacket.TEXT,
                (byte) 0,
                action
        );  //设置响应包基本信息
        packet.setContent(msg, charset);  //设置响应内容

        send(packet);  //发送消息包
    }

    // 发送二进制数据
    public void send(short action, byte[] msg) throws Exception{
        if (msg == null)
            send(action, msg, 0);
        else
            send(action, msg, msg.length);
    }

    public void send(short action, byte[] msg, int size) throws Exception{
        NetPacket packet = new NetPacket(
                NetPacket.BINARY,
                (byte) 0,
                action
        );
        packet.setContent(msg, size);

        send(packet);  //发送消息包
    }

    // 编码并发送一个消息包
    public void send(NetPacket packet) throws Exception{
        // 先发头部
        ByteBuffer headBuf = ByteBuffer.allocate(8);
//        设置头部内容
        headBuf.put(packet.getType());
        headBuf.put(packet.getFlag());
        headBuf.putShort(packet.getAction());
        headBuf.putInt(packet.getLength());

//            发送头部
        outputStream.write(headBuf.array(), 0, headBuf.position());

        // 发内容
        if (packet.getLength() > 0) {
            outputStream.write(packet.getContent(), 0, packet.getLength());
        }
    }


    ////////////// 数据包的接收 ////////////
    public NetPacket receive() {
        ByteBuffer headBuf = ByteBuffer.allocate(8);

        int rc = readFully(headBuf.array(), 0, 8);
        if (rc != 8)
            return null; // 当对方socket已经关闭时，我方无法感知,readFully()仍返回-1

        NetPacket packet = new NetPacket(
                headBuf.get(),
                headBuf.get(),
                headBuf.getShort()
        );
        int length = headBuf.getInt();  //获取内容的长度

        if (length > maxPacketSize) {
            // 此包不正常，length字段不会大
            System.out.println("收到AfNetPacket.length太大，可能是编码错误!" + length + ">" + maxPacketSize);
            return null;  //异常返回
        }

        if (length > 0) {
            byte[] content = new byte[length];
            rc = readFully(content, 0, length);
            if (rc != length)
                return null; // 当对方socket已经关闭时，我方无法感知,readFully()仍返回-1
            packet.setContent(content, length);
        }
        return packet;
    }

    // 完全接受N个字节：此方法会一直等待，直接收满N个字节才返回
    public int readFully(byte[] data, int off, int N) {
        int count = 0; // 已经读取字节数
        while (count < N) {
            int remain = N - count; // remain: 还要接收多少字节
            int numBytes = 0;

            try {
                numBytes = inputStream.read(data, off + count, remain);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("数据读入错误！");
            }

            if (numBytes < 0)
                return -1; // 对方socket已经关闭, 此read()并不会异常,而是返回-1

            count += numBytes;
        }
        return N;
    }
}
